/*
 * @Author: tankunpeng@fang.com
 * @Date: 2018-03-29 17:58:01
 * @Last Modified by: tankunpeng@fang.com
 * @Last Modified time: 2018-03-30 10:37:15
 * @Description: 初始化
 */

const co = require('co');
const prompt = require('co-prompt');
const chalk = require('chalk');
const path = require('path');
const fs = require('fs');
const mkdirp = require('mkdirp');
// const download = require('download-git-repo');
const MODE_0666 = parseInt('0666', 8);
const MODE_0755 = parseInt('0755', 8);
const _exit = process.exit;
const spawn = require('child_process').spawn;
const ora = require('ora');
let spinner;
const answerReg = /^\s*(y|yes|ok|true)\s*$/i;
const defaultReg = /^\s{0}$/i;
const cwd = process.cwd();
/**
 * Graceful exit for async STDIO
 */
function exit(code) {
    let draining = 0;
    const streams = [process.stdout, process.stderr];
    // flush output for Node.js Windows pipe bug
    // https://github.com/joyent/node/issues/6247 is just one bug example
    // https://github.com/visionmedia/mocha/issues/333 has a good discussion
    function done() {
        if (! draining--) _exit(code);
    }
    exit.exited = true;

    streams.forEach(function(stream) {
        // submit empty write request and wait for completion
        draining += 1;
        stream.write('', done);
    });

    done();
}

// Re-assign process.exit because of commander
process.exit = exit;

/**
 * Check if the given directory `path` is empty.
 * @param {String} path
 * @param {Function} fn
 */
function emptyDirectory(path) {
    return function(callback) {
        fs.readdir(path, function(err, files) {
            if (err && err.code !== 'ENOENT') {
                callback(err);
            } else {
                callback(null, !files || !files.length);
            }
        });
    };
}
/**
 * Check if the given file `path` is exists.
 * @param {string} path file path
 */
function existsFile(path) {
    return new Promise((resolve) => {
        fs.access(path, function(err) {
            if (err) {
                return resolve(false);
            }
            resolve(true);
        });
    });
}

/**
 * Create an app name from a directory path, fitting npm naming requirements.
 * @param {String} pathName
 */

function createAppName(pathName) {
    return path.basename(pathName)
        .replace(/[^A-Za-z0-9.()!~*'-]+/g, '-')
        .replace(/^[-_.]+|-+$/g, '')
        .toLowerCase();
}

/**
 * Determine if launched from cmd.exe
 */

function launchedFromCmd() {
    return process.platform === 'win32' && process.env._ === undefined;
}

/**
 * echo str > path.
 * @param {String} path
 * @param {String} str
 */
function write(path, str, mode) {
    fs.writeFileSync(path, str, {
        mode: mode || MODE_0666
    });
    console.log('   \x1b[36mcreate\x1b[0m : ' + path);
}


/**
 * Mkdir -p.
 * @param {String} path
 * @param {Function} fn
 */

function mkdir(path, fn) {
    mkdirp(path, MODE_0755, function(err) {
        if (err) throw err;
        console.log('   \x1b[36mcreate\x1b[0m : ' + path);
        fn && fn();
    });
}

/**
 * 终止构建
 */
function terminateBuild() {
    console.log('');
    console.log(chalk.yellow('terminate the build!'));
    console.log('');
    exit();
}

/**
 * Create application at the given directory `path`.
 * @param {String} dir
 * @param {String} inputFolder 输入文件夹
 * @param {String} outputFolder 输出文件夹
 */
function createApplication(name, dir, inputFolder, outputFolder, ops = {}) {
    console.log(chalk.cyan('\n Start generating...'));
    let wait = 5;
    const input = `${dir}/${inputFolder}`;
    const output = `${dir}/${outputFolder}`;
    /**
     * 完成后回调函数
     * @returns
     */
    const complete = function() {
        if (--wait) return;
        const prompt = launchedFromCmd() ? '>' : '$';
        console.log(chalk.gray(' prompt:'), chalk.green('  if installed automatically fails, you can also perform the following commands installed manually \n  ' + prompt + ' cd ' + dir + ' && npm install'));
        spinner = ora('Install npm packages...').start();
        const installCmd = (pack, ops) => {
            const install = spawn(pack, ops, {
                shell: true,
                cwd: dir
            });
            install.stdout.on('data', (data) => {
                spinner.info(data.toString());
            });

            install.stderr.on('data', (data) => {
                if (/command\s+not\s+found/ig.test(data.toString())) {
                    spinner.fail(data.toString());
                    console.log(chalk.cyan('\n Try using the NPM installation package...'));
                    installCmd('npm', ['install', '--registry=https://registry.npm.taobao.org']);
                }else {
                    spinner.warn(data.toString());
                }
            });

            install.on('close', (code) => {
                if (code === 0) {
                    spinner.succeed('Install successful');
                }
                exit(code);
            });
            install.on('error', function() {
                spinner.fail('Install failed');
                exit();
            });
        };
        console.log(chalk.cyan('\n Try using the CNPM installation package...'));
        installCmd('cnpm', ['install']);
    };

    mkdir(dir, function() {
        // config files
        co(function* () {
            const fangfisConfig = require('../project/fangfis.config.json');
            const targetPath = path.resolve(cwd, dir + '/fangfis.config.json');
            const hasConfigFile = yield existsFile(targetPath);
            let overridefile = true;
            let mergefile = false;
            const { yes } = ops;
            if (hasConfigFile) {
                if (yes) {
                    const localConfig = require(targetPath);
                    Object.assign(fangfisConfig, localConfig);
                }else {
                    const answer = yield prompt('file fangfis.config.json is exists, override file? [y/N]');
                    overridefile = answerReg.test(answer);
                    if (overridefile) {
                        const mergeAnswer = yield prompt('Merge fangfis.config.json files? [Y/n]');
                        mergefile = answerReg.test(mergeAnswer) || defaultReg.test(mergeAnswer);
                        if (mergefile) {
                            const localConfig = require(targetPath);
                            Object.assign(fangfisConfig, localConfig);
                        }
                    }
                }
            }

            if (overridefile) {
                const build = fangfisConfig.build;
                const upload = fangfisConfig.upload;
                build.input = `./${inputFolder}`;
                build.output = `./${outputFolder}`;
                build.base = `./${inputFolder}/js`;
                upload.default.input = upload.online.input = `./${outputFolder}`;
                write(targetPath, JSON.stringify(fangfisConfig, null, 2) + '\n');
                complete();
            }else {
                complete();
            }

            // 输入目录
            mkdir(input, function() {
                // eslint-disable-next-line max-nested-callbacks
                mkdir(`${input}/js`, () => {
                    write(`${input}/js/.gitkeep`, '');
                    complete();
                });
                // eslint-disable-next-line max-nested-callbacks
                mkdir(`${input}/css`, () => {
                    write(`${input}/css/.gitkeep`, '');
                    complete();
                });
                // eslint-disable-next-line max-nested-callbacks
                mkdir(`${input}/images`, () => {
                    write(`${input}/images/.gitkeep`, '');
                    complete();
                });
            });
            // 输出目录
            mkdir(output, () => complete());
            // package.json
            const pkg = require('../project/package.json');
            // write files
            write(`${dir}/package.json`, JSON.stringify(pkg, null, 2) + '\n');
        });
    });
}


/**
 * 获取格式化路径
 * @param {any} folder
 * @returns
 */
function getFormatFolder(folder) {
    const arr = folder.split('/');
    const reg = /^[.a-zA-Z][a-zA-Z_\-0-9]*$/gi;
    let foramt = '';
    for (let i = 0, len = arr.length; i < len; i++) {
        if (reg.test(arr[i])) {
            foramt = arr[i];
            break;
        }
    }
    return foramt;
}
module.exports = (ops) => {
    co(function*() {
        if (!exit.exited) {
            // Path
            let destinationPath;
            // 自定义输入路径
            let inputPath;
            // 自定义输出路径
            let outputPath;
            if (!ops.yes) {
                destinationPath = yield prompt('Project name (default:.): ');
                inputPath = yield prompt('Development environment folder name (default:dev): ');
                outputPath = yield prompt('Production environment folder name (default:static): ');
            }
            destinationPath = getFormatFolder(destinationPath || '.');
            inputPath = getFormatFolder(inputPath || 'dev');
            outputPath = getFormatFolder(outputPath || 'static');
            // App name
            const appName = createAppName(path.resolve(process.cwd(), destinationPath)) || 'fangfis-build';
            // Generate application
            const isEmpty = yield emptyDirectory(destinationPath);
            if (isEmpty) {
                createApplication(appName, destinationPath, inputPath, outputPath, ops);
            } else {
                let isContinue;
                if (!ops.yes) {
                    const answer = yield prompt('destination is not empty, continue? [Y/n]');
                    isContinue = answerReg.test(answer) || defaultReg.test(answer);
                } else {
                    isContinue = true;
                }
                if (isContinue) {
                    createApplication(appName, destinationPath, inputPath, outputPath, ops);
                } else {
                    terminateBuild();
                }
            }
        }
    });
};